<template>
    <div style="margin-top: 10px;">
        <!-- 上传区域 -->
        <el-upload ref="uploadRef" :auto-upload="true" multiple :action="uploadFileUrl" :http-request="uploadFile"
            :before-upload="beforeUpload" :on-exceed="handleExceed" :file-list="fileList" :limit="limit"
            :data="pramData" :headers="headers" :show-file-list="false">
            <el-button type="primary" v-if="!disabled">
                <el-icon>
                    <Upload />
                </el-icon>{{ buttonText }}</el-button>
            <template #tip>
                <!-- 上传提示 -->
                <div class="el-upload__tip" v-if="showTip && !disabled">
                    请上传
                    <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
                    <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
                    </template>
                    的文件
                </div>
            </template>
        </el-upload>
        <!-- 文件信息表格 -->
        <el-table v-if="fileList.length > 0" :data="fileList" border size="small">
            <!-- 表格列定义 -->
            <el-table-column prop="originalFileName" label="文件名" min-width="180" />
            <el-table-column label="大小" width="100">
                <template #default="scope">
                    {{ formatFileSize(scope.row.fileSize) }}
                </template>
            </el-table-column>
            <el-table-column label="进度" width="100" v-if="!disabled">
                <template #default="scope">
                    <el-progress :percentage="scope.row.percentage" status="success" />
                </template>
            </el-table-column>

            <el-table-column label="操作" width="130">
                <template #default="scope">
                    <el-button size="small" type="primary" v-if="isImage(scope.row.originalFileName)"
                        @click="handlePreview(scope.row)">
                        <el-icon>
                            <View />
                        </el-icon>
                    </el-button>
                    <el-button v-else-if="isPdf(scope.row.originalFileName)" size="small" type="primary"
                        @click="handlePdfPreview(scope.row)">
                        <el-icon>
                            <View />
                        </el-icon>
                    </el-button>
                    <el-button v-else size="small" type="warning"
                        @click="handleDownloadByUrl(scope.row.fileUrl, scope.row.originalFileName)">
                        <el-icon>
                            <Download />
                        </el-icon>
                    </el-button>
                    <el-button v-if="!disabled" size="small" type="danger"
                        @click="handleRemove(scope.row, scope.$index)">
                        <el-icon>
                            <Delete />
                        </el-icon>
                    </el-button>
                </template>
            </el-table-column>
        </el-table>
        <el-image-viewer v-if="imgDialogVisible" :url-list="[imgFileUrl]" @close="imgDialogVisible = false" />
    </div>
</template>

<script lang="js" setup>
import { ref, reactive, watch } from 'vue';
import { Download, View, Delete, Upload } from '@element-plus/icons-vue'
import { ElMessage, ElButton, ElProgress, ElImageViewer } from 'element-plus';
import { getToken } from "@/utils/auth"
import {
    formatFileSize,
    getFileIdShar256Value,
    uploadType,
    uploadFileLoading,
    uploadFileCloseLoading,
    handlePdfPreview,
    handleDownloadByUrl,
    uploadFileWithFetch
} from './utils/fileUtil'
import { isImage, isPdf } from './utils/fileType';
// 定义 Props
const props = defineProps({
    // 文件预览列表
    modelValue: [String, Object, Array],
    // 上传接口地址
    uploadFileUrl: { type: String, required: true, default: "/common/upload" },
    // 上传携带的参数
    pramData: { type: Object },
    // 数量限制
    limit: { type: Number, default: 3 },
    // 大小限制(MB)
    fileSize: { type: Number, default: 5 },
    fileType: {
        type: Array,
        default: () => ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf", "jpg", "jpeg", "png"]
    },
    // 上传按钮名称
    buttonText: { type: String, default: '选择文件' },
    // 是否显示提示
    showTip: { type: Boolean, default: true },
    // 禁用组件（仅查看文件）
    disabled: { type: Boolean, default: false }
});

const headers = reactive({
    //'Content-Type': 'multipart/form-data',
    'Authorization': 'Bearer ' + getToken()
})
const uploadRef = ref(null);
const fileList = ref([]);
const imgDialogVisible = ref(false);
const imgFileUrl = ref('');

const emit = defineEmits(['update:modelValue', 'upload-start', 'upload-complete']);

watch(() => props.modelValue, val => {
    if (val) {
        let temp = 1
        // 首先将值转为数组
        const list = Array.isArray(val) ? val : [props.modelValue]
        // 然后将数组转为对象数组
        fileList.value = list.map(item => {
            if (typeof item === "string") {
                item = { name: item, url: item }
            }
            item.uid = item.uid || new Date().getTime() + temp++
            return item
        })
    } else {
        fileList.value = []
        return []
    }
}, { deep: true, immediate: true })

const uploadFile = async (option) => {
    uploadFileLoading("文件上传中....");
    const { action, file, pramData } = option;
    const fileIdShar256 = await getFileIdShar256Value(file);
    const formData = new FormData();
    formData.append('file', file);
    formData.append('fileId', fileIdShar256);
    formData.append('fileName', file.name);
    formData.append('fileSize', file.size);
    formData.append('fileType', file.type);

    await uploadFileWithFetch(action, formData, pramData, headers)
        .then(res => {
            uploadFileCloseLoading();
            handleSuccess(file, res);
        })
        .catch(err => {
            uploadFileCloseLoading();
            handleError(err);
        });
    uploadFileCloseLoading();
};
/* 文件上传前的检查 */
function beforeUpload(file) {
    // 校检文件数量
    if (fileList.value.length >= props.limit) {
        ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
        return false;
    }
    // 校检文件类型
    if (props.fileType.length) {
        const fileName = file.name.split('.')
        const fileExt = fileName[fileName.length - 1]
        const isTypeOk = props.fileType.indexOf(fileExt) >= 0
        if (!isTypeOk) {
            ElMessage.error(`文件格式不正确，请上传${props.fileType.join("/")}格式文件!`)
            return false
        }
    }
    // 校检文件名是否包含特殊字符
    if (file.name.includes(',')) {
        ElMessage.error('文件名不正确，不能包含英文逗号!')
        return false
    }
    // 校检文件大小
    if (props.fileSize) {
        const isLt = file.size / 1024 / 1024 < props.fileSize
        if (!isLt) {
            ElMessage.error(`上传文件大小不能超过 ${props.fileSize} MB!`)
            return false
        }
    }
    return true
}
/* 文件删除 */
const handleRemove = (file, index) => {
    try {
        fileList.value.splice(index, 1);
        if (file.url) {
            URL.revokeObjectURL(file.url);//释放内存
        }
        emit("update:modelValue", fileList.value)
        ElMessage.success(`文件 "${file.name}" 已成功删除`);
    } catch (error) {
        ElMessage.error('删除文件时发生错误');
    }
};
/* 图片预览 */
const handlePreview = (file) => {
    imgDialogVisible.value = true;
    imgFileUrl.value = file.fileUrl;
};
/* 文件上传成功 */
const handleSuccess = (file, res) => {
    const fileInfo = {
        filePath: res.fileName,
        newFileName: res.newFileName || file.name,
        originalFileName: res.originalFileName || file.name,
        fileSize: file.size,
        fileType: file.type,
        fileUrl: res.url || URL.createObjectURL(file),
        status: uploadType.success,
        percentage: 100,
    }
    fileList.value.push(fileInfo);
    emit("update:modelValue", fileList.value);
};
// 文件个数超出
function handleExceed() {
    ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
}
// 上传失败
function handleError(err) {
    ElMessage.error("上传文件失败", err)
} 
</script>
<style scoped>
.el-upload__tip {
    font-size: 12px;
    color: #f30404;
}
</style>